使用AspectJ实现AOP

Aspect简介

AspectJ是一个基于Java语言的AOP框架

Spring2.0以后新增了对AspectJ切点表达式支持

@AspectJ是AspectJ 1.5新增的功能,通过JDK5注解技术,允许直接在Bean类中定义切面

新版本Spring框架,建议使用AspectJ方式来开发AOP

1
2
3
4
5
使用AspectJ需要引入Spring AOP和AspectJ相关Jar包:
spring-aop
aopalliance
spring-aspects
aspectjweaver

案例使用的代码:

https://github.com/hzebin/SpringAOP_AspectJ


使用AspectJ是,xml配置文件格式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">

<!--上面为aop的xml配置-->

<!--开启aspectJ的注解开发,自动代理-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>

<!--下面开始编写代码-->


</beans>

@AspectJ提供的通知类型

通知类型 说明
@Before 前置通知,相当于BeforeAdvice
@AfterReturning 后置通知,相当于AfterReturningAdvice
@Around 环绕通知,相当于AfterReturningAdvice
@AfterThrowing 异常抛出通知,相当于ThrowAdvice
@After 最终通知(相当于try-catch的finally),不管是否异常,该通知都会执行
@DeclareParents 引介通知,相当于IntroductionInterceptor

使用的例子(注解方式)

step1:创建Maven项目(Webapp)

step2:引入jar包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>cn.hzebin</groupId>
<artifactId>im_spring_aspect</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>

<name>im_spring_aspect Maven Webapp</name>
<!-- FIXME change it to the project's website -->
<url>http://www.example.com</url>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.7</maven.compiler.source>
<maven.compiler.target>1.7</maven.compiler.target>
</properties>

<dependencies>
<!--测试包-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>4.2.4.RELEASE</version>
</dependency>

<!--Spring开发包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.2.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.2.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>4.2.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>4.2.4.RELEASE</version>
</dependency>

<!--Spring AOP需要引入的包-->
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>4.2.4.RELEASE</version>
</dependency>

<!--AspectJ需要引入的包-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.5</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>4.2.4.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>compile</scope>
</dependency>
</dependencies>

<build>
<finalName>im_spring_aspect</finalName>
<pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
<plugins>
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.1.0</version>
</plugin>
<!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging -->
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.1</version>
</plugin>
<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>3.2.2</version>
</plugin>
<plugin>
<artifactId>maven-install-plugin</artifactId>
<version>2.5.2</version>
</plugin>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.8.2</version>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>

step3 - 1:创建目标类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package cn.hzebin.demo1;

public class ProductDao {
public void add() {
System.out.println("增加操作");
}

public void update() {
System.out.println("修改操作");
}

public void find() {
System.out.println("查询操作");
}

public void delete() {
System.out.println("删除操作");
}
}

step3 - 2:创建切面类

JoinPoint类可以获得切入点信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package cn.hzebin.demo1;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

/**
* AspectJ定义切面类,注解方式
*/
@Aspect //表明这是切面类
public class MyAspectAnno {

//前置通知
// @Before(value = "execution(* cn.hzebin.demo1.ProductDao.*(..))")
@Before(value = "execution(* cn.hzebin.demo1.ProductDao.add(..))")
public void before(JoinPoint joinPoint) {
//JoinPoint为获得切入点信息
System.out.println("前置通知" + joinPoint);
}
}

step4:创建applicationContext.xml配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">

<!--上面为aop的xml配置-->

<!--开启aspectJ的注解开发,自动代理-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>

<!--下面开始编写代码-->
<!--cn.hzebin.demo1-->

<!--目标类-->
<bean id="productDao" class="cn.hzebin.demo1.ProductDao"></bean>

<!--定义切面-->
<bean class="cn.hzebin.demo1.MyAspectAnno"></bean>
</beans>

step5:使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package cn.hzebin.demo1;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import javax.annotation.Resource;

/*
AspectJ 切面 使用案例
*/


@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class SpringDemo1 {
@Resource(name = "productDao")
private ProductDao productDao;

@Test
public void demo1() {
productDao.add();
productDao.delete();
productDao.find();
productDao.update();
}
}

@Before前置通知

可以在方法中传入JoinPoint对象,用来获得切点的信息

1
2
3
4
5
6
7
//前置通知
// @Before(value = "execution(* cn.hzebin.demo1.ProductDao.*(..))")
@Before(value = "execution(* cn.hzebin.demo1.ProductDao.add(..))")
public void before(JoinPoint joinPoint) {
//JoinPoint为获得切入点信息
System.out.println("==前置通知==,可以获得切入点信息" + joinPoint);
}

@AfterReturning后置通知

通过returning属性,可以定义方法返回值,作为参数

1
2
3
4
5
//后置通知
@AfterReturning(value = "execution(* cn.hzebin.demo1.ProductDao.update(..))", returning = "result")
public void demo2(Object result) {
System.out.println("==后置通知==,可以获得切入点的返回值 -> " + result);
}

@Around环绕通知

around方法的返回值就是目标代理方法执行返回值

参数为ProceedingJoinPoint,可以拦截目标方法的执行

1
2
3
4
5
6
7
8
//环绕通知
@Around(value = "execution(* cn.hzebin.demo1.ProductDao.delete(..))")
public Object demo3(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("环绕前的通知");
Object obj = joinPoint.proceed(); //执行目标方法,会有目标方法的返回值,可以不调用,则为拦截
System.out.println("环绕后的通知");
return obj;
}

@AfterThrowing异常通知

通过设置throwing属性,可以设置发生异常对象参数

1
2
3
4
5
//异常通知
@AfterThrowing(value = "execution(* cn.hzebin.demo1.ProductDao.find(..))", throwing = "e")
public void demo4(Throwable e) {
System.out.println("异常通知," + e.getMessage());
}

@After最终通知

无论是否发生异常,最终通知总是会被执行

1
2
3
4
5
//最终通知
@After(value = "execution(* cn.hzebin.demo1.ProductDao.findOne(..))")
public void demo5() {
System.out.println("最终通知");
}

@Pointcut可以给切点命名

  • 在每个通知内定义切点,会造成工作量大,不易维护,对于重复的切点,可以使用@Pointcut进行定义
  • 切点方法:private void 切点名() {}
  • 当通知多个切点时,可以使用||进行连接
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
package cn.hzebin.demo2;

import org.aspectj.lang.annotation.*;

/**
* 给切点命名,维护代码更加方便
*/
@Aspect
public class AspectAnno {
@Before(value = "myPointCut1() || myPointCut2()") //多个切点可以使用||进行连接
public void demo1() {
System.out.println("前置通知");
}

@AfterReturning(value = "myPointCut2()", returning = "result")
public void demo2(Object result) {
System.out.println("后置通知" + result);
}


//给切点命名
@Pointcut(value = "execution(* cn.hzebin.demo2.ProductDao.add(..))")
private void myPointCut1() {}

@Pointcut(value = "execution(* cn.hzebin.demo2.ProductDao.delete(..))")
private void myPointCut2() {}

@Pointcut(value = "execution(* cn.hzebin.demo2.ProductDao.update(..))")
private void myPointCut3() {}

@Pointcut(value = "execution(* cn.hzebin.demo2.ProductDao.findOne(..))")
private void myPointCut4() {}

@Pointcut(value = "execution(* cn.hzebin.demo2.ProductDao.findAll(..))")
private void myPointCut5() {}
}

Aspect的XML配置

== 【unfinish】 ==